home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / osrc.arc / FTPSERV.C < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-22  |  15.2 KB  |  646 lines

  1. /* FTP Server state machine - see RFC 959 */
  2. #include <stdio.h>
  3. #include <ctype.h>
  4. #include <time.h>
  5. #include "global.h"
  6. #include "mbuf.h"
  7. #include "socket.h"
  8. #include "ftp.h"
  9. #include "ftpserv.h"
  10. #include "proc.h"
  11.  
  12. void ftpserv();
  13. char *pathname();
  14. static int pport(),ftplogin(),sendit(),recvit();
  15. extern char Hostname[],Version[];
  16.  
  17. /* Command table */
  18. static char *commands[] = {
  19.     "user",
  20. #define    USER_CMD    0
  21.     "acct",
  22. #define    ACCT_CMD    1
  23.     "pass",
  24. #define    PASS_CMD    2
  25.     "type",
  26. #define    TYPE_CMD    3
  27.     "list",
  28. #define    LIST_CMD    4
  29.     "cwd",
  30. #define    CWD_CMD        5
  31.     "dele",
  32. #define    DELE_CMD    6
  33.     "name",
  34. #define    NAME_CMD    7
  35.     "quit",
  36. #define    QUIT_CMD    8
  37.     "retr",
  38. #define    RETR_CMD    9
  39.     "stor",
  40. #define    STOR_CMD    10
  41.     "port",
  42. #define    PORT_CMD    11
  43.     "nlst",
  44. #define    NLST_CMD    12
  45.     "pwd",
  46. #define    PWD_CMD        13
  47.     "xpwd",            /* For compatibility with 4.2BSD */
  48. #define    XPWD_CMD    14
  49.     "mkd ",
  50. #define    MKD_CMD        15
  51.     "xmkd",            /* For compatibility with 4.2BSD */
  52. #define    XMKD_CMD    16
  53.     "xrmd",            /* For compatibility with 4.2BSD */
  54. #define    XRMD_CMD    17
  55.     "rmd ",
  56. #define    RMD_CMD        18
  57.     "stru",
  58. #define    STRU_CMD    19
  59.     "mode",
  60. #define    MODE_CMD    20
  61.     NULLCHAR
  62. };
  63.  
  64. /* Response messages */
  65. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  66. static char badcmd[] = "500 Unknown command\r\n";
  67. static char unsupp[] = "500 Unsupported command or option\r\n";
  68. static char givepass[] = "331 Enter PASS command\r\n";
  69. static char logged[] = "230 Logged in\r\n";
  70. static char typeok[] = "200 Type %s OK\r\n";
  71. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  72. static char deleok[] = "250 File deleted\r\n";
  73. static char mkdok[] = "200 MKD ok\r\n";
  74. static char delefail[] = "550 Delete failed: %s\r\n";
  75. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  76. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  77. static char badport[] = "501 Bad port syntax\r\n";
  78. static char unimp[] = "502 Command not yet implemented\r\n";
  79. static char bye[] = "221 Goodbye!\r\n";
  80. static char nodir[] = "553 Can't read directory \"%s\": %s\r\n";
  81. static char cantopen[] = "550 Can't read file \"%s\": %s\r\n";
  82. static char sending[] = "150 Opening data connection for %s %s\r\n";
  83. static char cantmake[] = "553 Can't create \"%s\": %s\r\n";
  84. static char writerr[] = "552 Write error: %s\r\n";
  85. static char portok[] = "200 Port command okay\r\n";
  86. static char rxok[] = "226 File received OK\r\n";
  87. static char txok[] = "226 File sent OK\r\n";
  88. static char noperm[] = "550 Permission denied\r\n";
  89. static char noconn[] = "425 Data connection reset\r\n";
  90. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  91. static char okay[] = "200 Ok\r\n";
  92.  
  93. static int Sftp = -1;    /* Prototype socket for service */
  94.  
  95. /* Start up FTP service */
  96. ftpstart(argc,argv)
  97. int argc;
  98. char *argv[];
  99. {
  100.     struct sockaddr_in lsocket;
  101.     int s;
  102.  
  103.     psignal(Curproc,0);    /* Don't keep the parser waiting */
  104.     chname(Curproc,"FTP listener");
  105.  
  106.     lsocket.sin_family = AF_INET;
  107.     lsocket.sin_addr.s_addr = Ip_addr;
  108.     if(argc < 2)
  109.         lsocket.sin_port = IPPORT_FTP;
  110.     else
  111.         lsocket.sin_port = atoi(argv[1]);
  112.  
  113.     Sftp = socket(AF_INET,SOCK_STREAM,0);
  114.     bind(Sftp,(char *)&lsocket,sizeof(lsocket));
  115.     listen(Sftp,1);
  116.     for(;;){
  117.         if((s = accept(Sftp,NULLCHAR,(int *)NULL)) == -1)
  118.             break;    /* Service is shutting down */
  119.  
  120.         /* Spawn a server */
  121.         newproc("ftpserv",2048,ftpserv,s,NULL,NULL);
  122.     }
  123. }
  124. void
  125. ftpserv(s,unused)
  126. int s;    /* Socket with user connection */
  127. void *unused;
  128. {
  129.     struct ftpserv ftp;
  130.     char **cmdp,buf[512],*arg,*cp,*cp1,*file,*mode;
  131.     long t;
  132.     int cnt,i;
  133.     struct sockaddr_in socket;
  134. #ifndef    CPM
  135.     FILE *dir();
  136. #endif
  137.  
  138.     memset((char *)&ftp,0,sizeof(ftp));    /* Start with clear slate */
  139.     ftp.data = -1;
  140.  
  141.     sockowner(s,Curproc);        /* We own it now */
  142.     ftp.control = s;
  143.     /* Set default data port */
  144.     i = SOCKSIZE;
  145.     getpeername(s,(char *)&socket,&i);
  146.     socket.sin_port = IPPORT_FTPD;
  147.     ASSIGN(ftp.port,socket);
  148.  
  149.     log(s,"open FTP");
  150.     time(&t);
  151.     cp = ctime(&t);
  152.     if((cp1 = strchr(cp,'\n')) != NULLCHAR)
  153.         *cp1 = '\0';
  154.     usprintf(s,banner,Hostname,Version,cp);
  155. loop:    if((cnt = recvline(s,buf,sizeof(buf))) == -1){
  156.         /* He closed on us */
  157.         goto finish;
  158.     }
  159.     if(cnt == 0){
  160.         /* Can't be a legal FTP command */
  161.         usprintf(ftp.control,badcmd);
  162.         goto loop;
  163.     }    
  164.     rip(buf);
  165. #ifdef    UNIX
  166.     /* Translate first word to lower case */
  167.     for(cp = buf;*cp != ' ' && *cp != '\0';cp++)
  168.         *cp = tolower(*cp);
  169. #else
  170.     /* Translate entire buffer to lower case */
  171.     for(cp = buf;*cp != '\0';cp++)
  172.         *cp = tolower(*cp);
  173. #endif
  174.     /* Find command in table; if not present, return syntax error */
  175.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  176.         if(strncmp(*cmdp,buf,strlen(*cmdp)) == 0)
  177.             break;
  178.     if(*cmdp == NULLCHAR){
  179.         usprintf(ftp.control,badcmd);
  180.         goto loop;
  181.     }
  182.     /* Allow only USER, PASS and QUIT before logging in */
  183.     if(ftp.cd == NULLCHAR || ftp.path == NULLCHAR){
  184.         switch(cmdp-commands){
  185.         case USER_CMD:
  186.         case PASS_CMD:
  187.         case QUIT_CMD:
  188.             break;
  189.         default:
  190.             usprintf(ftp.control,notlog);
  191.             goto loop;
  192.         }
  193.     }
  194.     arg = &buf[strlen(*cmdp)];
  195.     while(*arg == ' ')
  196.         arg++;
  197.  
  198.     /* Execute specific command */
  199.     switch(cmdp-commands){
  200.     case USER_CMD:
  201.         free(ftp.username);
  202.         if((ftp.username = strdup(arg)) == NULLCHAR){
  203.             shutdown(ftp.control,1);
  204.             goto finish;
  205.         }
  206.         usprintf(ftp.control,givepass);
  207.         break;
  208.     case TYPE_CMD:
  209.         switch(arg[0]){
  210.         case 'A':
  211.         case 'a':    /* Ascii */
  212.             ftp.type = ASCII_TYPE;
  213.             usprintf(ftp.control,typeok,arg);
  214.             break;
  215.         case 'l':
  216.         case 'L':
  217.             while(*arg != ' ' && *arg != '\0')
  218.                 arg++;
  219.             if(*arg == '\0' || *++arg != '8'){
  220.                 usprintf(ftp.control,only8);
  221.                 break;
  222.             }
  223.             ftp.type = LOGICAL_TYPE;
  224.             ftp.logbsize = 8;
  225.             usprintf(ftp.control,typeok,arg);
  226.             break;
  227.         case 'B':
  228.         case 'b':    /* Binary */
  229.         case 'I':
  230.         case 'i':    /* Image */
  231.             ftp.type = IMAGE_TYPE;
  232.             usprintf(ftp.control,typeok,arg);
  233.             break;
  234.         default:    /* Invalid */
  235.             usprintf(ftp.control,badtype,arg);
  236.             break;
  237.         }
  238.         break;
  239.     case QUIT_CMD:
  240.         usprintf(ftp.control,bye);
  241.         goto finish;
  242.     case RETR_CMD:
  243.         file = pathname(ftp.cd,arg);
  244.         switch(ftp.type){
  245.         case IMAGE_TYPE:
  246.         case LOGICAL_TYPE:
  247.             mode = READ_BINARY;
  248.             break;
  249.         case ASCII_TYPE:
  250.             mode = READ_TEXT;
  251.             break;
  252.         }
  253.         if(!permcheck(&ftp,RETR_CMD,file)){
  254.              usprintf(ftp.control,noperm);
  255.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  256.             usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
  257.         } else {
  258.             log(ftp.control,"RETR %s",file);
  259.             sendit(&ftp,"RETR",file);
  260.         }
  261.         free(file);
  262.         break;
  263.     case STOR_CMD:
  264.         file = pathname(ftp.cd,arg);
  265.         switch(ftp.type){
  266.         case IMAGE_TYPE:
  267.         case LOGICAL_TYPE:
  268.             mode = WRITE_BINARY;
  269.             break;
  270.         case ASCII_TYPE:
  271.             mode = WRITE_TEXT;
  272.             break;
  273.         }
  274.         if(!permcheck(&ftp,STOR_CMD,file)){
  275.              usprintf(ftp.control,noperm);
  276.         } else if((ftp.fp = fopen(file,mode)) == NULLFILE){
  277.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  278.         } else {
  279.             log(ftp.control,"STOR %s",file);
  280.             recvit(&ftp,"STOR",file);
  281.         }
  282.         free(file);
  283.         break;
  284.     case PORT_CMD:
  285.         if(pport(&ftp.port,arg) == -1){
  286.             usprintf(ftp.control,badport);
  287.         } else {
  288.             usprintf(ftp.control,portok);
  289.         }
  290.         break;
  291. #ifndef CPM
  292.     case LIST_CMD:
  293.         file = pathname(ftp.cd,arg);
  294.         if(!permcheck(&ftp,RETR_CMD,file)){
  295.              usprintf(ftp.control,noperm);
  296.         } else if((ftp.fp = dir(file,1)) == NULLFILE){
  297.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  298.         } else {
  299.             sendit(&ftp,"LIST",file);
  300.         }
  301.         free(file);
  302.         break;
  303.     case NLST_CMD:
  304.         file = pathname(ftp.cd,arg);
  305.         if(!permcheck(&ftp,RETR_CMD,file)){
  306.              usprintf(ftp.control,noperm);
  307.         } else if((ftp.fp = dir(file,0)) == NULLFILE){
  308.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  309.         } else {
  310.             sendit(&ftp,"NLST",file);
  311.         }
  312.         free(file);
  313.         break;
  314.     case CWD_CMD:
  315.         file = pathname(ftp.cd,arg);
  316.         if(!permcheck(&ftp,RETR_CMD,file)){
  317.              usprintf(ftp.control,noperm);
  318.             free(file);
  319. #ifdef    MSDOS
  320.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  321.         } else if(strcmp(file,"/") == 0 || access(file,0) == 0){
  322. #else
  323.         } else if(access(file,0) == 0){    /* See if it exists */
  324. #endif
  325.             /* Succeeded, record in control block */
  326.             free(ftp.cd);
  327.             ftp.cd = file;
  328.             usprintf(ftp.control,pwdmsg,file);
  329.         } else {
  330.             /* Failed, don't change anything */
  331.             usprintf(ftp.control,nodir,file,sys_errlist[errno]);
  332.             free(file);
  333.         }
  334.         break;
  335.     case XPWD_CMD:
  336.     case PWD_CMD:
  337.         usprintf(ftp.control,pwdmsg,ftp.cd);
  338.         break;
  339. #else
  340.     case LIST_CMD:
  341.     case NLST_CMD:
  342.     case CWD_CMD:
  343.     case XPWD_CMD:
  344.     case PWD_CMD:
  345. #endif
  346.     case ACCT_CMD:        
  347.         usprintf(ftp.control,unimp);
  348.         break;
  349.     case DELE_CMD:
  350.         file = pathname(ftp.cd,arg);
  351.         if(!permcheck(&ftp,DELE_CMD,file)){
  352.              usprintf(ftp.control,noperm);
  353.         } else if(unlink(file) == 0){
  354.             usprintf(ftp.control,deleok);
  355.         } else {
  356.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  357.         }
  358.         free(file);
  359.         break;
  360.     case PASS_CMD:
  361.         ftplogin(&ftp,arg);            
  362.         break;
  363. #ifndef    CPM
  364.     case XMKD_CMD:
  365.     case MKD_CMD:
  366.         file = pathname(ftp.cd,arg);
  367.         if(!permcheck(&ftp,MKD_CMD,file)){
  368.             usprintf(ftp.control,noperm);
  369.         } else if(mkdir(file,0777) == 0){
  370.             usprintf(ftp.control,mkdok);
  371.         } else {
  372.             usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
  373.         }
  374.         free(file);
  375.         break;
  376.     case XRMD_CMD:
  377.     case RMD_CMD:
  378.         file = pathname(ftp.cd,arg);
  379.         if(!permcheck(&ftp,RMD_CMD,file)){
  380.              usprintf(ftp.control,noperm);
  381.         } else if(rmdir(file) == 0){
  382.             usprintf(ftp.control,deleok);
  383.         } else {
  384.             usprintf(ftp.control,delefail,sys_errlist[errno]);
  385.         }
  386.         free(file);
  387.         break;
  388.     case STRU_CMD:
  389.         if(tolower(arg[0]) != 'f')
  390.             usprintf(ftp.control,unsupp);
  391.         else
  392.             usprintf(ftp.control,okay);
  393.         break;
  394.     case MODE_CMD:
  395.         if(tolower(arg[0]) != 's')
  396.             usprintf(ftp.control,unsupp);
  397.         else
  398.             usprintf(ftp.control,okay);
  399.         break;
  400.     }
  401. #endif
  402.     goto loop;
  403. finish:
  404.     log(ftp.control,"close FTP");
  405.     /* Clean up */
  406.     close_s(ftp.control);
  407.     if(ftp.data != -1)
  408.         close_s(ftp.data);
  409.     if(ftp.fp != NULLFILE)
  410.         fclose(ftp.fp);
  411.     free(ftp.username);
  412.     free(ftp.path);
  413.     free(ftp.cd);
  414. }
  415.  
  416. /* Shut down FTP server */
  417. ftp0()
  418. {
  419.     if(Sftp != -1)
  420.         close_s(Sftp);
  421.     Sftp = -1;
  422. }
  423. static
  424. int
  425. pport(sock,arg)
  426. struct sockaddr_in *sock;
  427. char *arg;
  428. {
  429.     int32 n;
  430.     int i;
  431.  
  432.     n = 0;
  433.     for(i=0;i<4;i++){
  434.         n = atoi(arg) + (n << 8);
  435.         if((arg = strchr(arg,',')) == NULLCHAR)
  436.             return -1;
  437.         arg++;
  438.     }
  439.     sock->sin_addr.s_addr = n;
  440.     n = atoi(arg);
  441.     if((arg = strchr(arg,',')) == NULLCHAR)
  442.         return -1;
  443.     arg++;
  444.     n = atoi(arg) + (n << 8);
  445.     sock->sin_port = n;
  446.     return 0;
  447. }
  448. /* Attempt to log in the user whose name is in ftp->username and password
  449.  * in pass
  450.  */
  451. static
  452. ftplogin(ftp,pass)
  453. struct ftpserv *ftp;
  454. char *pass;
  455. {
  456.     char buf[80],*cp,*cp1;
  457.     FILE *fp;
  458.     int anony = 0;
  459.  
  460.     if((fp = fopen(Userfile,READ_TEXT)) == NULLFILE){
  461.         /* Userfile doesn't exist */
  462.         usprintf(ftp->control,noperm);
  463.         return;
  464.     }
  465.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  466.         if(buf[0] == '#')
  467.             continue;    /* Comment */
  468.         if((cp = strchr(buf,' ')) == NULLCHAR)
  469.             /* Bogus entry */
  470.             continue;
  471.         *cp++ = '\0';        /* Now points to password */
  472.         if(strcmp(ftp->username,buf) == 0)
  473.             break;        /* Found user name */
  474.     }
  475.     if(feof(fp)){
  476.         /* User name not found in file */
  477.         fclose(fp);
  478.         usprintf(ftp->control,noperm);
  479.         return;
  480.     }
  481.     fclose(fp);
  482.     /* Look for space after password field in file */
  483.     if((cp1 = strchr(cp,' ')) == NULLCHAR){
  484.         /* Invalid file entry */
  485.         usprintf(ftp->control,noperm);
  486.         return;
  487.     }
  488.     *cp1++ = '\0';    /* Now points to path field */
  489.     if(strcmp(cp,"*") == 0)
  490.         anony = 1;    /* User ID is password-free */
  491.     if(!anony && strcmp(cp,pass) != 0){
  492.         /* Password required, but wrong one given */
  493.         usprintf(ftp->control,noperm);
  494.         return;
  495.     }
  496.     if((cp = strchr(cp1,' ')) == NULLCHAR){
  497.         /* Permission field missing */
  498.         usprintf(ftp->control,noperm);
  499.         return;
  500.     }
  501.     *cp++ = '\0';    /* now points to permission field */
  502.  
  503.     /* Set up current directory and path prefix */
  504.     ftp->cd = strdup(cp1);
  505.     ftp->path = strdup(cp1);
  506.     
  507.     /* And finally set the permission bits */
  508.     ftp->perms = atoi(cp);
  509.  
  510.     usprintf(ftp->control,logged);
  511.     if(!anony)
  512.         log(ftp->control,"%s logged in",ftp->username);
  513.     else
  514.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  515. }        
  516.  
  517. #ifdef    MSDOS
  518. /* Illegal characters in a DOS filename */
  519. char badchars[] = "\"[]:|<>+=;,";
  520. #endif
  521.  
  522. /* Return 1 if the file operation is allowed, 0 otherwise */
  523. permcheck(ftp,op,file)
  524. struct ftpserv *ftp;
  525. int op;
  526. char *file;
  527. {
  528.     char *cp;
  529.  
  530.     if(file == NULLCHAR || ftp->path == NULLCHAR)
  531.         return 0;    /* Probably hasn't logged in yet */
  532. #ifdef    MSDOS
  533.     /* Check for characters illegal in MS-DOS file names */
  534.     for(cp = badchars;*cp != '\0';cp++){
  535.         if(strchr(file,*cp) != NULLCHAR)
  536.             return 0;    
  537.     }
  538. #endif
  539. #if    (defined(AMIGA) || defined(MAC))
  540. #else
  541.     /* The target file must be under the user's allowed search path */
  542.     if(strncmp(file,ftp->path,strlen(ftp->path)) != 0)
  543.         return 0;
  544. #endif
  545.  
  546.     switch(op){
  547.     case RETR_CMD:
  548.         /* User must have permission to read files */
  549.         if(ftp->perms & FTP_READ)
  550.             return 1;
  551.         return 0;
  552.     case DELE_CMD:
  553.     case RMD_CMD:
  554.         /* User must have permission to (over)write files */
  555.         if(ftp->perms & FTP_WRITE)
  556.             return 1;
  557.         return 0;
  558.     case STOR_CMD:
  559.     case MKD_CMD:
  560.         /* User must have permission to (over)write files, or permission
  561.          * to create them if the file doesn't already exist
  562.          */
  563.         if(ftp->perms & FTP_WRITE)
  564.             return 1;
  565.         if(access(file,2) == -1 && (ftp->perms & FTP_CREATE))
  566.             return 1;
  567.         return 0;
  568.     }
  569.     return 0;    /* "can't happen" -- keep lint happy */
  570. }
  571. static 
  572. sendit(ftp,command,file)
  573. struct ftpserv *ftp;
  574. char *command;
  575. char *file;
  576. {
  577.     long total;
  578.     struct sockaddr_in dport;
  579.  
  580.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  581.     dport.sin_family = AF_INET;
  582.     dport.sin_addr.s_addr = Ip_addr;
  583.     dport.sin_port = IPPORT_FTPD;
  584.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  585.     usprintf(ftp->control,sending,command,file);
  586.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  587.         fclose(ftp->fp);
  588.         usprintf(ftp->control,noconn);
  589.         close_s(ftp->data);
  590.         ftp->data = -1;
  591.         return;
  592.     }
  593.     /* Do the actual transfer */
  594.     total = sendfile(ftp->fp,ftp->data,ftp->type);
  595.  
  596.     if(total == -1){
  597.         /* An error occurred on the data connection */
  598.         usprintf(ftp->control,noconn);
  599.         shutdown(ftp->data,2);    /* Blow away data connection */
  600.     } else {
  601.         usprintf(ftp->control,txok);
  602.     }
  603.     fclose(ftp->fp);
  604.     close_s(ftp->data);
  605.     ftp->data = -1;
  606. }
  607. static
  608. recvit(ftp,command,file)
  609. struct ftpserv *ftp;
  610. char *command;
  611. char *file;
  612. {
  613.     struct sockaddr_in dport;
  614.     long total;
  615.  
  616.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  617.     dport.sin_family = AF_INET;
  618.     dport.sin_addr.s_addr = Ip_addr;
  619.     dport.sin_port = IPPORT_FTPD;
  620.     bind(ftp->data,(char *)&dport,SOCKSIZE);
  621.     usprintf(ftp->control,sending,command,file);
  622.     if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
  623.         fclose(ftp->fp);
  624.         usprintf(ftp->control,noconn);
  625.         close_s(ftp->data);
  626.         ftp->data = -1;
  627.         return;
  628.     }
  629.     total = recvfile(ftp->fp,ftp->data,ftp->type);
  630.  
  631. #ifdef    CPM
  632.     if(ftp->type == ASCII_TYPE)
  633.         fputc(CTLZ,ftp->fp);
  634. #endif
  635.     if(total == -1) {
  636.         /* An error occurred while writing the file */
  637.         usprintf(ftp->control,writerr,sys_errlist[errno]);
  638.         shutdown(ftp->data,2);    /* Blow it away */
  639.     } else {
  640.         usprintf(ftp->control,rxok);
  641.         close_s(ftp->data);
  642.     }
  643.     ftp->data = -1;
  644.     fclose(ftp->fp);
  645. }
  646.